from django.conf import settings
import base64
import requests
import json
import logging
import time
from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    retry_if_exception_type,
    before_sleep_log
)
from dataclasses import dataclass
from typing import Optional, Tuple

logger = logging.getLogger('apps')

# 错误码映射表（根据百度API文档补充完整）
ERROR_CODE_MAPPING = {
    3300: "输入参数不正确",
    3301: "音频质量过差",
    3302: "Token过期",
    3303: "请求频率超出限制",
    3304: "服务额度不足",
    3305: "自定义模型异常",
    3307: "音频解码失败",
    3308: "语音过长",
    3309: "无效Token",
    3310: "音频采样率不符",
    3311: "音频格式不支持",
    3312: "音频数据不正确",
}


@dataclass
class AudioValidationResult:
    is_valid: bool
    message: Optional[str] = None


class VoiceServiceError(Exception):
    """语音服务基础异常"""

    def __init__(self, message: str, code: str, http_status: int = 500):
        self.code = code
        self.http_status = http_status
        super().__init__(message)


class RateLimitExceededError(VoiceServiceError):
    """频率限制异常"""

    def __init__(self):
        super().__init__(
            "请求过于频繁，请稍后再试",
            code="RATE_LIMIT_EXCEEDED",
            http_status=429
        )


class VoiceService:
    """语音服务类（增强版）"""

    def __init__(self, api_key: str, secret_key: str):
        self.api_key = api_key
        self.secret_key = secret_key
        self.token = self._get_token()
        self.config = settings.AI_CONFIG.get('voice_service', {})
        self._init_default_config()

        # 请求频率控制
        self.request_interval = self.config.get('min_request_interval', 2)
        self.last_request_time = 0

    def _init_default_config(self):
        """初始化默认配置"""
        default_config = {
            'max_file_size': 10 * 1024 * 1024,  # 10MB
            'allowed_formats': ['m4a', 'wav', 'pcm'],  # 支持的音频格式
            'sample_rate': 16000,  # 采样率固定为16k
            'max_retries': 5,
            'request_timeout': 15,
            'channel': 1,  # 固定为单声道
        }
        for key, value in default_config.items():
            self.config.setdefault(key, value)

    @retry(
        stop=stop_after_attempt(5),
        wait=wait_exponential(multiplier=2, min=1, max=60),
        retry=(
            retry_if_exception_type(RateLimitExceededError) |
            retry_if_exception_type(requests.exceptions.RequestException)
        ),
        before_sleep=before_sleep_log(logger, logging.WARNING)
    )
    def recognize_audio(self, audio_file, language: str = 'zh') -> str:
        """识别音频文件（增强版）"""
        try:
            # 频率控制
            self._enforce_rate_limit()

            # 验证音频文件
            validation = self.validate_audio(audio_file)
            if not validation.is_valid:
                raise VoiceServiceError(
                    validation.message,
                    code="INVALID_AUDIO",
                    http_status=400
                )

            # 读取音频内容
            audio_content = audio_file.read()
            file_ext = audio_file.name.split('.')[-1].lower()

            # 针对不同格式使用不同的上传方式
            if file_ext == 'amr':
                # AMR 格式使用 JSON 方式上传（改回 JSON 方式）
                request_data = {
                    "format": "amr",
                    "rate": 8000,    # AMR 使用 8kHz 采样率
                    "channel": 1,     # 固定单声道
                    "token": self.token,
                    "cuid": "notebook_app",
                    "len": len(audio_content),
                    "speech": base64.b64encode(audio_content).decode('utf-8'),
                    "dev_pid": 1537   # 固定使用普通话模型
                }

                # 调试日志
                debug_data = {
                    **request_data,
                    'speech': '(base64 encoded audio data)',
                    'file_name': audio_file.name,
                    'file_size': audio_file.size
                }
                logger.debug(f"AMR请求数据: {json.dumps(debug_data, ensure_ascii=False)}")

                # 发送请求
                response = requests.post(
                    "https://vop.baidu.com/server_api",
                    headers={'Content-Type': 'application/json'},
                    json=request_data,
                    timeout=self.config['request_timeout']
                )
            else:
                # 其他格式使用 JSON 方式上传
                request_data = {
                    "format": file_ext,
                    "rate": 16000,
                    "channel": 1,
                    "token": self.token,
                    "cuid": "notebook_app",
                    "len": len(audio_content),
                    "speech": base64.b64encode(audio_content).decode('utf-8'),
                    "dev_pid": 1537 # 普通话
                }

                # 调试日志
                debug_data = {
                    **request_data,
                    'speech': '(base64 encoded audio data)',
                    'file_name': audio_file.name,
                    'file_size': audio_file.size
                }
                logger.debug(f"请求数据: {json.dumps(debug_data, ensure_ascii=False)}")

                # 发送请求
                response = requests.post(
                    "https://vop.baidu.com/server_api",
                    headers={'Content-Type': 'application/json'},
                    json=request_data,
                    timeout=self.config['request_timeout']
                )

            # 记录响应
            result = response.json()
            logger.debug(f"API响应: {json.dumps(result, ensure_ascii=False)}")

            if result.get('err_no') == 0:
                return result.get('result', [''])[0]

            # 处理错误
            error_code = result.get('err_no')
            error_msg = ERROR_CODE_MAPPING.get(error_code, "未知错误")
            
            # 根据错误码提供具体的错误信息
            if error_code in [3305, 3310, 3311, 3312]:  # 音频相关错误
                logger.error(
                    "音频识别失败: 错误码=%d, 消息=%s, 格式=%s, 大小=%d字节",
                    error_code,
                    error_msg,
                    file_ext,
                    audio_file.size
                )
                
                error_messages = {
                    3305: "音频格式不正确或质量过低",
                    3310: "音频采样率不符合要求",
                    3311: "不支持的音频格式",
                    3312: "音频数据不完整或已损坏"
                }
                
                suggestion = "\n请尝试：\n" \
                            "1. 使用 WAV 格式 (16kHz采样率)\n" \
                            "2. 确保音频质量清晰，无杂音\n" \
                            "3. 控制音频长度在 2-30 秒之间\n" \
                            "4. 重新录制音频"
                
                raise VoiceServiceError(
                    f"{error_messages.get(error_code, error_msg)}{suggestion}",
                    code="AUDIO_ERROR",
                    http_status=400
                )
            
            raise VoiceServiceError(
                f"{error_msg}({error_code})",
                code=f"API_ERROR_{error_code}",
                http_status=400
            )

        except Exception as e:
            logger.error(f"音频识别异常: {str(e)}", exc_info=True)
            raise

    def _enforce_rate_limit(self):
        """执行请求频率控制"""
        current_time = time.time()
        elapsed = current_time - self.last_request_time
        if elapsed < self.request_interval:
            sleep_time = self.request_interval - elapsed
            logger.debug(f"请求频率控制，等待 {sleep_time:.2f} 秒")
            time.sleep(sleep_time)
        self.last_request_time = time.time()

    def _build_request_data(self, audio_content: bytes, language: str) -> dict:
        """构建请求参数"""
        # 获取音频格式，默认为 pcm
        audio_format = getattr(self, '_audio_format', 'pcm')
        
        return {
            "format": audio_format,  # 使用实际的音频格式
            "rate": 16000,  # 固定使用16k采样率
            "channel": 1,  # 单声道
            "token": self.token,
            "cuid": "notebook_app",
            "len": len(audio_content),
            "speech": base64.b64encode(audio_content).decode('utf-8'),
            "dev_pid": 1537  # 固定使用普通话模型
        }

    def _process_response(self, result: dict) -> str:
        """处理API响应"""
        if result.get('err_no') == 0:
            return result.get('result')[0]

        error_code = result.get('err_no')
        error_msg = ERROR_CODE_MAPPING.get(error_code, "未知错误")

        # 处理需要重试的错误
        if error_code == 3302:  # Token过期
            self.token = self._get_token()
            raise VoiceServiceError(
                error_msg,
                code="TOKEN_EXPIRED",
                http_status=401
            )
        elif error_code == 3303:  # 频率限制
            raise RateLimitExceededError()

        # 其他业务错误
        raise VoiceServiceError(
            f"{error_msg}({error_code})",
            code=f"API_ERROR_{error_code}",
            http_status=400
        )

    def _handle_http_error(self, error: requests.HTTPError) -> None:
        """处理HTTP错误"""
        status_code = error.response.status_code
        if status_code == 429:
            retry_after = error.response.headers.get('Retry-After', 60)
            logger.warning(f"触发频率限制，建议重试间隔: {retry_after}秒")
            raise RateLimitExceededError()

        raise VoiceServiceError(
            f"服务请求失败: {str(error)}",
            code=f"HTTP_{status_code}",
            http_status=status_code
        )

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=2, max=10),
        before_sleep=before_sleep_log(logger, logging.WARNING)
    )
    def _get_token(self) -> str:
        """获取访问令牌（增强版）"""
        try:
            response = requests.get(
                "https://aip.baidubce.com/oauth/2.0/token",
                params={
                    "grant_type": "client_credentials",
                    "client_id": self.api_key,
                    "client_secret": self.secret_key
                },
                timeout=10
            )
            response.raise_for_status()
            token_data = response.json()

            if 'access_token' in token_data:
                return token_data['access_token']

            raise VoiceServiceError(
                token_data.get('error_description', '未知错误'),
                code="TOKEN_FETCH_FAILED",
                http_status=401
            )

        except requests.RequestException as e:
            raise VoiceServiceError(
                f"令牌获取失败: {str(e)}",
                code="NETWORK_ERROR",
                http_status=503
            )

    def validate_audio(self, audio_file) -> AudioValidationResult:
        """增强版音频验证"""
        try:
            # 文件大小验证
            if audio_file.size > self.config['max_file_size']:
                return AudioValidationResult(
                    False,
                    f"文件大小超过限制（最大 {self.config['max_file_size'] // 1024 // 1024}MB）"
                )

            # 文件格式验证
            file_ext = audio_file.name.split('.')[-1].lower()
            
            # 更新支持的格式和对应的采样率
            format_info = {
                'wav': {'rate': 16000, 'desc': 'WAV (16kHz)'},
                'pcm': {'rate': 16000, 'desc': 'PCM (16kHz)'},
                'amr': {'rate': 8000, 'desc': 'AMR (8kHz)'},
                'm4a': {'rate': 16000, 'desc': 'M4A (16kHz)'}
            }
            
            if file_ext not in format_info:
                formats_desc = ', '.join(f"{info['desc']}" for info in format_info.values())
                return AudioValidationResult(
                    False,
                    f"不支持的文件格式。支持的格式: {formats_desc}"
                )

            # 记录音频信息
            logger.info(
                "音频文件信息: 名称=%s, 格式=%s, 预期采样率=%dHz, 大小=%d字节",
                audio_file.name,
                file_ext,
                format_info[file_ext]['rate'],
                audio_file.size
            )

            return AudioValidationResult(True)

        except AttributeError:
            return AudioValidationResult(False, "无效的文件对象")
        except Exception as e:
            logger.error(f"音频验证异常: {str(e)}", exc_info=True)
            return AudioValidationResult(False, "文件验证失败")

    @staticmethod
    def _get_language_code(language: str) -> int:
        """获取语言代码（扩展版）"""
        language_codes = {
            'zh': 1537,  # 普通话(纯中文识别)
            'en': 1737,  # 英语
            'ct': 1637,  # 粤语
            'sz': 1837,  # 四川话
        }
        return language_codes.get(language.lower(), 1537)